package com.gaoyi.crm.util;

import static com.gaoyi.crm.constant.Constants.TAG;

import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebSettings;

import androidx.annotation.RequiresApi;

import com.gaoyi.crm.callback.Callback;
import com.gaoyi.crm.constant.Constants;
import com.gaoyi.crm.constant.SPName;
import com.gaoyi.crm.db.entity.HttpCache;
import com.gaoyi.crm.repository.CacheRepository;

import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Call;
import okhttp3.Dispatcher;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;

public class OKHttpUtil {
    private static volatile OKHttpUtil INSTANCE;
    private static final long MAX_SIZE = 8 * 1024 * 1024;
    public final static String NET_URL = "http://synergosdemo.ddns.net/SynManufCRM";
//    public final static String NET_URL = "http://184.168.123.134/SynManufCRM";
//    public final static String NET_URL = "http://192.168.1.188:5681";
    public final static String IMG_URL = NET_URL + "/api/utils/images/400/";
    private static final String VERSION_TOKEN = "56efbbf00223e8872e935752c2537b88";
    private final OkHttpClient mClient;
    private static Handler mHandler;
    private final String mImei;
    private String mUserAgent;
    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    public void post(Runnable runnable){
        mHandler.post(runnable);
    }

    public OKHttpUtil() {
        mImei = AppUtil.getIMEI();
        mHandler = new Handler(Looper.getMainLooper());
        File dir = AppUtil.getContext().getFilesDir();
        Cache cache = new Cache(dir, MAX_SIZE);
        Dispatcher dispatcher = new Dispatcher();
        dispatcher.setMaxRequests(30);
        dispatcher.setMaxRequestsPerHost(10);
        mClient = new OkHttpClient.Builder()
                .dispatcher(dispatcher)
                .cache(cache)
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .callTimeout(60, TimeUnit.SECONDS)
                .addInterceptor(new CacheInterceptor())
                .addInterceptor(new TokenInterceptor())
                .addNetworkInterceptor(new HttpCacheInterceptor())
                .retryOnConnectionFailure(false)
                .build();
    }

    public static OKHttpUtil getInstance() {
        if (INSTANCE == null) {
            synchronized (OKHttpUtil.class) {
                if (INSTANCE == null) {
                    INSTANCE = new OKHttpUtil();
                }
            }
        }
        return INSTANCE;
    }

    private String getUserAgent() {
        if (TextUtils.isEmpty(mUserAgent)) {
            String userAgent;
            try {
                userAgent = WebSettings.getDefaultUserAgent(AppUtil.getContext());
            } catch (Exception e) {
                userAgent = System.getProperty("http.agent");
            }
            if (userAgent == null) {
                return "";
            }
            StringBuilder sb = new StringBuilder();
            for (int i = 0, length = userAgent.length(); i < length; i++) {
                char c = userAgent.charAt(i);
                if (c <= '\u001f' || c >= '\u007f') {
                    sb.append(String.format("\\u%04x", (int) c));
                } else {
                    sb.append(c);
                }
            }
            mUserAgent = sb.toString();
        }
        return mUserAgent;
    }

    private void callOnFailure(Callback callback, IOException e) {
        Log.d(Constants.TAG, "result fail");
        callback.onFailed(e);
    }

    public void getAsync(final String url, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            Log.d(TAG, url);
            Request request = new Request.Builder()
                    .url(url)
                    .get()
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(SPName.TOKEN))
                    .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                    .addHeader("imei", mImei)
//                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getVersion())
                    .build();
            mClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    mHandler.post(() -> callOnFailure(callback, e));
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        final String result = response.body().string();
                        Log.d(TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            mHandler.post(() -> callback.onFailed(new IOException()));
                        } else {
                            try {
                                Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                mHandler.post(() -> callback.onSuccess(o));
                            } catch (Exception e) {
                                mHandler.post(() -> callback.onFailed(e));
                                e.printStackTrace();
                            }

                        }
                    } catch (IOException e) {
                        mHandler.post(() -> callback.onFailed(e));
                    }
                }
            });
        });
    }

    public void postAsync(String url, Object json, Callback callback, Type typeofT) {
        postAsync(url, GsonUtil.getInstance().toJson(json), callback, typeofT);
    }

    public void postAsync(final String url, final String json, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            RequestBody body = RequestBody.create(json, JSON);
            Log.d(Constants.TAG, json);
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(SPName.TOKEN))
                    .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                    .addHeader("imei", mImei)
//                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getVersion())
                    .post(body)
                    .build();
            mClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    mHandler.post(() -> callOnFailure(callback, e));
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        final String result = response.body().string();
                        Log.d(Constants.TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            Log.d(Constants.TAG, "result empty");
                            mHandler.post(() -> callback.onFailed(new IOException()));
                        } else {
                            try {
                                Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                mHandler.post(() -> callback.onSuccess(o));
                            } catch (Exception e) {
                                e.printStackTrace();
                                mHandler.post(() -> callback.onFailed(e));
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        mHandler.post(() -> callback.onFailed(e));
                    }
                }
            });
        });
    }

    public void postSyncThread(String url, Object json, Callback callback, Type typeofT) {
        postSyncThread(url, GsonUtil.getInstance().toJson(json), callback, typeofT);
    }

    public void postSyncThread(final String url, final String json, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            RequestBody body = RequestBody.create(json, JSON);
            Log.d(TAG, json);
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(SPName.TOKEN))
                    .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                    .addHeader("imei", mImei)
                    .post(body)
                    .build();
            mClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    callOnFailure(callback, e);
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        final String result = response.body().string();
                        Log.d(TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            Log.d(TAG, "result empty");
                            callback.onFailed(new IOException());
                        } else {
                            try {
                                Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                callback.onSuccess(o);
                            } catch (Exception e) {
                                e.printStackTrace();
                                callback.onFailed(e);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        callback.onFailed(e);
                    }
                }
            });
        });
    }

    /**
     * 上傳文件
     */
    public void uploadFileAsync(String url, String filePath, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            File file = new File(filePath);
            RequestBody fileBody = RequestBody.Companion.create(file, MediaType.parse("application/octet-stream"));
            RequestBody requestBody = new MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("file", file.getName(), fileBody)
                    .build();
            final Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(SPName.TOKEN))
                    .post(requestBody)
                    .build();
            mClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    mHandler.post(() -> callOnFailure(callback, e));
                }

                @Override
                public void onResponse(@NotNull final Call call, @NotNull Response response) {
                    try {
                        String result = response.body().string();
                        Log.d(Constants.TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            Log.d(Constants.TAG, "result empty");
                            mHandler.post(() -> callback.onFailed(new IOException()));
                        } else {
                            try {
                                Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                mHandler.post(() -> callback.onSuccess(o));
                            } catch (Exception e) {
                                mHandler.post(() -> callback.onFailed(e));
                            }
                        }
                    } catch (IOException e) {
                        mHandler.post(() -> callback.onFailed(e));
                    }
                }
            });
        });
    }
}

class CacheInterceptor implements Interceptor {

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) throws IOException {
        Request request = chain.request();
        Response response;
        String url = request.url().toString();
        if (url.equals(OKHttpUtil.NET_URL + "/api/users/login")
                || url.equals(OKHttpUtil.NET_URL + "/api/palBillEvents/add")
                || url.equals(OKHttpUtil.NET_URL + "/api/palBillEvents/repResult")
                || url.equals(OKHttpUtil.NET_URL + "/api/palBills/cancel")
                || url.equals(OKHttpUtil.NET_URL + "/api/palBills/patrol")
                || url.equals(OKHttpUtil.NET_URL + "/api/palBills/approval")
                || url.equals(OKHttpUtil.NET_URL + "/api/users/switchArea")
                || url.equals(OKHttpUtil.NET_URL + "/api/utils/uploadfile")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/users/forgotPwd?eMail=")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/WebServices/download/")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/WebServices/appInfo?applicationId=")) {
            response = getResponse(chain, CacheControl.FORCE_NETWORK);
        } else {
            if (request.method().equals("POST")) {
                Buffer buffer = new Buffer();
                request.body().writeTo(buffer);
                String params = buffer.readString(StandardCharsets.UTF_8);
                if (NetworkUtil.isConnected()) {
                    try {
                        Response originalResponse = chain.proceed(request);
                        MediaType type = originalResponse.body().contentType();
                        byte[] bs = originalResponse.body().bytes();
                        response = originalResponse.newBuilder()
                                .removeHeader("Pragma")
                                .removeHeader("Cache-Control")
                                .header("Cache-Control", "public, max-age=" + 60 * 60 * 24)
                                .body(ResponseBody.Companion.create(bs, type))
                                .build();
                        CacheRepository repository = new CacheRepository();
                        repository.insertCache(new HttpCache(url, params, new String(bs, "GB2312")));
                    } catch (IOException e) {
                        response = getCacheResponse(url, params, request);
                        e.printStackTrace();
                    }
                } else {
                    response = getCacheResponse(url, params, request);
                }
            } else {
                if (NetworkUtil.isConnected()) {
                    try {
                        Response originalResponse = chain.proceed(request);
                        originalResponse.close();
                        response = getResponse(chain, CacheControl.FORCE_NETWORK);
                    } catch (IOException e) {
                        response = getResponse(chain, CacheControl.FORCE_CACHE);
                        e.printStackTrace();
                    }
                } else {
                    response = getResponse(chain, CacheControl.FORCE_CACHE);
                }
            }
        }
        return response;
    }

    private Response getCacheResponse(String url, String params, Request request) {
        CacheRepository repository = new CacheRepository();
        String b = repository.getCacheResponse(url, params);
        return new Response.Builder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 28)
                .body(ResponseBody.Companion.create(b.getBytes(), OKHttpUtil.JSON))
                .request(request)
                .protocol(Protocol.HTTP_1_1)
                .code(200)
                .message("")
                .build();
    }

    private Response getResponse(Chain chain, CacheControl forceCache) throws IOException {
        return chain.proceed(chain.request()
                .newBuilder()
                .cacheControl(forceCache)
                .build()
        );
    }
}

class HttpCacheInterceptor implements Interceptor {

    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        return response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, max-age=" + 24 * 30 * 6000)
                .build();
    }
}
